Model file language ==================== A RISE model lives in a plain-text file with the extension ``.rs``. The file is read by the RISE parser when you call ``dsge_model('mymodel.rs')`` and is converted into a model object on which the engines (solve, estimate, filter, forecast, ...) operate. This chapter is the reference for everything you can write in that file. The model file describes the *model*: which variables exist, which parameters they depend on, and what equations they satisfy. It does **not** carry calibration, priors, or solve-time choices -- those live in companion MATLAB files or as options on the relevant calls. See *What does not go in the model file* below. .. contents:: :local: :depth: 2 Block keywords at a glance --------------------------- A ``.rs`` file is a sequence of blocks, each introduced by an ``@`` keyword. The blocks may appear in any order; the parser collects them and links them together. .. list-table:: :header-rows: 1 :widths: 25 75 * - Keyword - Purpose * - ``@endogenous`` - Declare endogenous variables. * - ``@exogenous`` - Declare exogenous innovations (shocks). * - ``@parameters`` - Declare parameters whose value does not switch. * - ``@parameters(chain,N[,names...])`` - Declare parameters that switch across ``N`` regimes of a Markov chain. * - ``@observables`` - Declare the variables observed in estimation / filtering. * - ``@model`` - The list of equations. Required. * - ``@steady_state_model`` - Closed-form steady state (optional but recommended for non-trivial models). * - ``@transition_functions`` - Endogenous (time-varying) transition probabilities -- one equation per off-diagonal entry per chain. * - ``@optimization_problem`` - Replace one or more equations with the first-order conditions of a planner's problem (optimal policy). * - ``@epilogue`` - Equations evaluated *after* solve and filter, e.g. for diagnostics or to derive variables not needed in the solution. A complete minimal example -- a three-equation New Keynesian model with two shocks -- appears at the end of this chapter. Declarations ------------- Endogenous variables ~~~~~~~~~~~~~~~~~~~~~ Variables determined inside the model:: @endogenous y pi i You can attach a comment to a variable; comments survive in the solution table and in plots:: @endogenous Y "Output", C "Consumption", PAI "Inflation", R "Interest rate (net)" Variables in logs. Tag a variable to be interpreted in logs by adding ``(log)`` to the declaration:: @endogenous(log) Y C @endogenous PAI R The shorter form is one line per group, but you may also mix them in the same block:: @endogenous Y "Output" C "Consumption" @endogenous(log) MU "Technology growth" A bulk form is available for the case where most variables are in logs:: @log_variables @all_but MU R means *every endogenous variable is in logs except* ``MU`` and ``R``. The ``@endogenous(log)`` form is preferred when only a few variables are in logs; ``@log_variables @all_but`` is the older bulk form, kept for backwards compatibility. Exogenous variables (shocks) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ White-noise innovations -- one per Gaussian shock you want to feed into the model:: @exogenous eps_m "Monetary shock", eps_a "Technology shock" Parameters ~~~~~~~~~~~ Non-switching parameters:: @parameters beta sigma kappa phi_pi phi_y rho_g std_eps_m std_eps_g Switching parameters ~~~~~~~~~~~~~~~~~~~~~ A parameter that takes different values in different regimes of a Markov chain is declared with ``@parameters(chain, N)`` where ``N`` is the number of states of the chain:: @parameters(mon,2) phi_pi @parameters(fis,2) gamma_b This declares two chains -- ``mon`` and ``fis`` -- each with two states. ``phi_pi`` takes two values; ``gamma_b`` takes two values. The model has ``2 x 2 = 4`` composite regimes. You can label the regimes inline:: @parameters(zlb,2,"normal times","lower bound") gam The labels appear in solution tables and plots. The chain is introduced by its first appearance as the first argument of ``@parameters(chain,N)``; the user names it there. The transition probabilities are declared either as ordinary parameters (constant probabilities) or as equations in a ``@transition_functions`` block (endogenous / time-varying probabilities); see *Markov chains* below. Observables ~~~~~~~~~~~~ The variables you observe in the data when filtering or estimating:: @observables y pi i There must be at least as many shocks as observables, otherwise the predicted observation covariance becomes singular ("stochastic singularity"). The @model block ----------------- The ``@model`` block contains the equations. Each equation ends with ``;``. Equations may span several lines. Time indexing ~~~~~~~~~~~~~~ Variables are indexed by time. Three equivalent notations are available; the ``{...}`` form is recommended for readability:: x{t} x{t-1} x{t+1} % explicit time index x x{-1} x{+1} % t implicit, offsets explicit x x(-1) x(+1) % parenthesis form A *steady-state* value of a variable is written ``{stst}``:: R{stst} % steady-state value of R Equation labels ~~~~~~~~~~~~~~~~ A quoted string immediately before an equation labels it. The label appears in error messages, in printed solutions, and in historical decompositions:: "Dynamic IS curve" x{t} = x{t+1} - (1/sigma)*(i{t} - pi{t+1}) + g{t}; "Phillips curve" pi{t} = beta*pi{t+1} + kappa*x{t}; Auxiliary definitions ~~~~~~~~~~~~~~~~~~~~~~ A line that begins with ``#`` inside ``@model`` introduces a short-hand. It is substituted into subsequent equations at parse time:: @model # rho_theta = rho_e; # phi = (thetass-1)/psi; # sig_theta = sig_e*phi; log(THETA) = (1-rho_theta)*log(thetass) + rho_theta*log(THETA{-1}) + sig_theta*EPS_THETA; Definitions can reference each other in declaration order and may use any parameter or steady-state value already declared. Steady-state-form companion equations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ A dynamic equation may be followed by ``#`` and a simpler *steady-state form*, separated from the rest of the equation by a ``#`` token and terminated by the same ``;``. RISE uses the steady-state form when computing the deterministic steady state, which often converges much faster than evaluating the full dynamic equation at the steady state:: "Euler equation" -B*LAMBDA + betad*B{+1}*LAMBDA{+1}*(1+R)/PAI{+1} = 0 # 1+R = PAI*exp(MU)/betad ; Both forms have to be mathematically consistent at the steady state. Comments ~~~~~~~~~ ``%`` starts a line comment. Block comments are not supported. Steady state ------------- A model file describes the *model*; the **steady state belongs in a separate function**. The reason is the same as the reason calibration belongs in a separate ``*_params.m`` file: a model and its steady state are two different things, and either can change without the other. Conflating them inside the ``.rs`` file is bad practice. Steady-state file (recommended) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Write a MATLAB function -- the name is your choice -- that takes the parameter struct (and the model object, if you need it), populates the steady-state values, and returns the updated parameter struct. Point RISE at it via the ``sstate_file`` option:: m = set(m, sstate_file = 'my_steady_state'); There is no enforced naming convention: ``my_steady_state``, ``compute_ss``, ``model_ss``, anything works. The convention ``_sstate.m`` is a habit, not a requirement. The steady-state file is **regime-agnostic**: it sees the parameter struct as is, returns the bare-name steady-state values, and does not branch on chain states. Any parameter implied by a steady-state condition (a "steady-state-derived" parameter) belongs in the steady-state file, not as a numeric in the ``*_params.m`` file. ``@steady_state_model`` block (in-file alternative) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The steady state may also be written directly inside the model file in a ``@steady_state_model`` block:: @steady_state_model A = 1; Y = (thetass-1)/thetass*(zss-beta*gam)/(zss-gam); C = Y; ... The block is *evaluated*, not solved -- the right-hand side may reference parameters and earlier steady-state expressions in declaration order. The separate steady-state file is the recommended pattern; the in-file block is available when keeping everything in one file is the user's preference. No steady-state specification ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ When neither a ``sstate_file`` nor a ``@steady_state_model`` is provided, RISE attempts a numerical steady-state solve from a default initial guess of zero. This works for log-linearized models whose steady state is zero by construction. Markov chains -------------- RISE handles Markov-switching as a first-class concept. A chain is introduced by writing parameters that switch on it (via ``@parameters(chain,N)``) and by declaring its transition probabilities -- either as constant parameters or as equations in a ``@transition_functions`` block. Constant transition probabilities ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ For a chain ``mon`` with ``N`` states, the off-diagonal transition probabilities are declared as ordinary parameters with the name pattern ``mon_tp__``:: @parameters mon_tp_1_2 mon_tp_2_1 @parameters(mon,2) phi_pi The diagonal probabilities ``mon_tp_1_1`` and ``mon_tp_2_2`` are inferred (each row of the transition matrix must sum to ``1``) and must **not** be declared explicitly. Calibrate them like any other parameter:: p.mon_tp_1_2 = 0.05; p.mon_tp_2_1 = 0.05; p.phi_pi_mon_1 = 2.5; p.phi_pi_mon_2 = 0.7; m = set(m, parameters = p); Endogenous transition probabilities ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ When the probabilities depend on endogenous variables (TVTP), declare them as equations in a ``@transition_functions`` block:: @parameters p_min "transition-prob floor", p_max "transition-prob ceiling", theta_zlb "sensitivity to log(B)" @parameters(zlb,2,"normal times","lower bound") gam @transition_functions "Probability normal -> bound: rises as preference demand falls" zlb_tp_1_2 = p_min + (p_max - p_min) / (1 + B^theta_zlb); "Probability bound -> normal: rises as preference demand recovers" zlb_tp_2_1 = p_min + (p_max - p_min) / (1 + B^(-theta_zlb)); The right-hand side may reference any endogenous variable, parameter, or steady-state value. Keep the probabilities strictly interior (``0 < p < 1``) -- a transition-function that touches zero or one makes the chain absorbing and is rarely intended. Occasionally-binding constraints ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ An OBC is a Markov chain whose switching is determined *endogenously* by the binding state of the constraint, not by exogenous probabilities. Declare the chain in the usual way:: @parameters(zlb,2) bind with the constraint encoded in the model:: i{t} = bind*i_floor + (1-bind)*i_taylor{t}; and tell ``solve`` to honor the constraint at solve time (see *Modeling -> Solving*). The transition probabilities of an OBC chain must be zero (the switching is endogenous, not stochastic); ``solve`` enforces this. Optimal policy --------------- When you want one equation in the model to be replaced by the first-order conditions of a planner's problem -- i.e., to choose an instrument optimally rather than according to a rule -- declare an ``@optimization_problem`` block. Single-player problem ~~~~~~~~~~~~~~~~~~~~~~ :: @optimization_problem{ @objective = -0.5*(pi^2 + lambda_y*y^2 + lambda_i*i^2), @discount = beta } @model % no Taylor rule -- i is the planner's instrument, chosen % to minimize the discounted sum of -objective y{t} = y{t+1} - (1/sigma)*(i{t} - pi{t+1}) + g{t}; pi{t} = beta*pi{t+1} + kappa*y{t}; ... The instrument is whichever endogenous variable does not appear on the left-hand side of any equation in ``@model``. The choice between commitment and discretion is made at solve time:: m = solve(m, solve_policy_type = "ramsey"); % commitment m = solve(m, solve_policy_type = "discretion"); There is no ``@commitment`` parameter and no ``@``-prefixed marker in the model file -- the solve choice is an option, not a model property. Multi-player problem (Nash, Stackelberg) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Two players with separate objectives and instruments:: @optimization_problem[@no_u_turn=false]{ Monetary: @order = 1 @discount = beta @objective = -0.5*(pi^2 + lambda_y*y^2 + lambda_i*i^2) @instrument = i ; Fiscal: @order = 2 @discount = beta @objective = -0.5*(tau^2 + lambda_b*b^2) @instrument = tau ; } When all players have the same ``@order``, the game is Nash. When the orders differ, it is a Stackelberg cascade with the lower ``@order`` as the leader. Epilogue --------- Equations that are not needed to solve the model but should be evaluated *after* solve and filter -- diagnostics, accounting identities, output transformations -- live in an ``@epilogue`` block:: @epilogue "Annualised inflation" pi_ann = 400*pi; "Real interest rate" r_real = i - pi{+1}; Epilogue equations may reference any endogenous variable but they do *not* affect the solution. They are evaluated after the fact, which keeps the dynamic system small. What does not go in the model file ------------------------------------ The model file describes the *model*. By the modern convention several things are kept *out*: Numerical parameter values ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Calibration lives in a companion MATLAB file (e.g. ``mymodel_params.m``) that returns a struct of parameter values:: function p = mymodel_params() p = struct(); p.beta = 0.99; p.sigma = 1.0; p.kappa = 0.30; ... end and is bound to the model with ``set``:: m = set(m, parameters = mymodel_params()); This keeps model and calibration on independent change cycles. The ``.rs`` file may also carry a ``@parameterization`` block; the separate ``*_params.m`` file is the recommended pattern. Steady-state values ~~~~~~~~~~~~~~~~~~~~ Same reasoning, applied to the steady state. The steady-state function (a plain MATLAB file, name of your choice) is bound via ``sstate_file``:: m = set(m, sstate_file = 'my_steady_state'); The ``@steady_state_model`` block inside the model file is also supported when keeping everything in one file is the user's preference. See *Steady state* above. Solve-time choices ~~~~~~~~~~~~~~~~~~~ Choices about *how* the model is solved -- commitment vs discretion, perturbation strategy, solver, solve paradigm, ... are options on the ``solve`` call, not declarations in the model file:: m = solve(m, solver = '+mn', solve_policy_type = "ramsey"); The model file does not change when you switch from commitment to discretion. Estimation priors ~~~~~~~~~~~~~~~~~~ Priors live in the same companion ``_params.m`` (or in a dedicated ``_priors.m``) as a separate output of the same function:: function [p, priors] = mymodel_est_params() p = struct(); p.beta = 0.99; ... priors = struct(); priors.kappa = {0.30, 0.10, 0.60, 'beta_pdf(0.9)', 1e-6, 0.999}; ... end and are bound with:: m = set(m, parameters = p, estim_priors = priors); Priors may also be declared inside the ``.rs`` file's ``@parameterization`` block; the separate ``*_est_params.m`` file is the recommended pattern, since priors are an estimation choice rather than a model property.